/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package pl3;

import java.util.HashMap;
import java.util.HashSet;
import java.util.StringTokenizer;

/**
 * Represents a Syntactic Analisys Table.
 * 
 * @author Luis Alberto Pérez García <luixal@gmail.com>
 * @version 0.1
 */
public class SyntacticAnalisysTable {

    private String id;
    private HashMap<String, String> SATable;

    /**
     * Default class constructor.
     */
    public SyntacticAnalisysTable() {
        this.id = "SAT";
        this.SATable = new HashMap<String, String>();
    }
    
    /**
     * Parametrized class constructor.
     *
     * @param id The ID for the table
     */
    public SyntacticAnalisysTable(String id) {
        this.id = id;
        this.SATable = new HashMap<String, String>();
    }


    /**
     * Copy class constructor.
     * @param sat The SyntacticAnalisysTable object used to construct this one.
     */
    public SyntacticAnalisysTable(SyntacticAnalisysTable sat) {
        this.id = sat.getId();
        this.SATable = new HashMap<String, String>(sat.getSATable());
    }

    /**
     * Checks if the position given by <b>X</b> and <b>Y</b> is a valid position.
     * Method: Checks that X is a non.terminal and Y is terminal.
     *
     * @param x A <b>non-terminal</b> element used as coordinate X in the position
     * @param y A <b>terminal</b> element used as coordinate Y in the position
     * @return True if the position is valid. False any other way.
     */
    public boolean isValidPosition(String x, String y) {
        return (!Utils.isTerminal(x) && Utils.isTerminal(y));
    }

    /**
     * Checks if the position given by <b>X</b> and <b>Y</b> contains a value.
     *
     * @param x A <b>non-terminal</b> element used as coordinate X in the position
     * @param y A <b>terminal</b> element used as coordinate Y in the position
     * @return True is the position contains a value. False any other way.
     */
    public boolean contains(String x, String y) {
        if (x != null && y != null) {
            return (this.isValidPosition(x, y) && this.SATable.containsKey(x + "," + y));
        } else {
            return false;
        }
    }
    
    /**
     * Retrieves the value for a current position in the table.
     *
     * @param x A <b>non-terminal</b> element used as coordinate X in the position
     * @param y A <b>terminal</b> element used as coordinate Y in the position
     * @return Value at position <b>x</b>,<b>y</b>
     */
    public String get(String x, String y) {
        if (this.contains(x, y)) {
            return this.SATable.get(x + "," + y);
        } else {
            return "ERROR";
        }
    }

    /**
     * Puts a value in the table.
     *
     * @param x A <b>non-terminal</b> element used as coordinate X in the position
     * @param y A <b>terminal</b> element used as coordinate Y in the position
     * @param value Value to be stored in the table
     */
    public void put(String x, String y, String value) {
        if (this.isValidPosition(x, y)) {
            this.SATable.put(x + "," + y, value);
        }
    }

    /**
     * Removes a value from the table.
     *
     * @param x A <b>non-terminal</b> element used as coordinate X in the position
     * @param y A <b>terminal</b> element used as coordinate Y in the position
     */
    public void remove(String x, String y) {
        if (this.contains(x, y)) {
            this.SATable.remove(x + "," + y);
        }
    }

    /**
     * Checks if the value at position x,y is an error.
     *
     * @param x A <b>non-terminal</b> element used as coordinate X in the position
     * @param y A <b>terminal</b> element used as coordinate Y in the position
     * @return True if the value is <i>ERROR</i>. False if it's a valid value.
     */
//    public boolean isError(String x, String y) {
//        if (this.contains(x, y)) {
//            return this.get(x, y).equals("ERROR");
//        } else {
//            return false;
//        }
//    }
    public boolean isError(String x, String y) {
        return !this.contains(x, y);
    }

    // Inmediate methods:
    /**
     * Removes all elements in the table.
     */
    public void clear() {
        this.SATable.clear();
    }

    /**
     * Retrieves the number of values stored in the table.
     *
     * @return Number of values stored in the table.
     */
    public Integer size() {
        return this.SATable.size();
    }

    /**
     * Checks if the table is empty.
     *
     * @return True if the table is empty. False any other way.
     */
    public boolean isEmpty() {
        return this.SATable.isEmpty();
    }
    
    // getters&setters:

    /**
     * Returns the table.
     *
     * @return A HashMap containing the table.
     */
    protected HashMap<String, String> getSATable() {
        return SATable;
    }

    /**
     * Sets a new table.
     *
     * @param SATable A HashMap containing a new table to be assigned to this one.
     */
    protected void setSATable(HashMap<String, String> SATable) {
        this.SATable = SATable;
    }

    /**
     * Retrieves the ID of the table.
     *
     * @return The ID of the table.
     */
    public String getId() {
        return this.id;
    }

    /**
     * Sets the ID of the table.
     *
     * @param id The ID to be assigned to the table.
     */
    public void setId(String id) {
        this.id = id;
    }

    /**
     * Returns a list of terminal indexes.
     *
     * @return Terminal indexes
     */
    public HashSet<String> getTerminals() {
        HashSet<String> result = new HashSet<String>();
        for (String s:this.SATable.keySet()) {
            StringTokenizer stk = new StringTokenizer(s, ",");
            stk.nextToken();
            result.add(stk.nextToken());
        }
        return result;
    }

    /**
     * Returns a list of non-terminal indexes.
     *
     * @return Non-terminal indexes
     */
    public HashSet<String> getNonTerminals() {
        HashSet<String> result = new HashSet<String>();
        for (String s:this.SATable.keySet()) {
            StringTokenizer stk = new StringTokenizer(s, ",");
            result.add(stk.nextToken());
        }
        return result;
    }

    /**
     * Returns the Syntactic Analisys Table in an array format.
     *
     * @return SAT table in an array of Strings.
     */
    public String[][] toArray() {
        // local variables needed:
        String[] terminals = new String[this.getTerminals().size()+1];
        terminals = this.getTerminals().toArray(terminals);
        String[] nonTerminals = new String[this.getNonTerminals().size()+1];
        nonTerminals = this.getNonTerminals().toArray(nonTerminals);
        // creating the array:
        String[][] result = new String[nonTerminals.length+1][terminals.length+1];
        // populating array headers:
        result[0][0] = "";
        for (int i = 0; i < nonTerminals.length; i++) {
            result[i+1][0] = nonTerminals[i];
        }
        for (int i = 0; i < terminals.length; i++) {
            result[0][i+1] = terminals[i];
        }
        // populating array content:
        for (int i = 0; i < nonTerminals.length; i++) {
            for (int j = 0; j < terminals.length; j++) {
                result[i+1][j+1] = this.get(nonTerminals[i], terminals[j]);
                if (result[i+1][j+1].equals("ERROR")) {
                    result[i+1][j+1] = "--";
                }
            }
        }
        // returning the array:
        System.out.println(result.length);
        return result;
    }

    /**
     * Shows the table in a formatted representation.
     *
     * @return Text representation of the table.
     */
    @Override
    public String toString() {
        String result = "";
        String[][] array = this.toArray();
        for (int i = 0; i < this.getNonTerminals().size()+1; i++) {
            for (int j = 0; j < this.getTerminals().size()+1; j++) {
                result += "\t" + array[i][j];
            }
            result += "\n";
        }
        return result;
    }

    /**
     * Main method for testing purpouses
     * 
     * @param args
     */
    public static void main(String[] args) {
        System.out.println();
        System.out.println("Creating a new table with some values...");
        SyntacticAnalisysTable sat = new SyntacticAnalisysTable("MySAT");
        sat.put("A", "a", "Aaasdf");
        sat.put("B", "b", "Bbasdf");
        sat.put("C", "c", "Ccasdf");
        sat.put("D", "d", "Ddasdf");
        sat.put("D", "+", "D+asdf");
//        System.out.println("Showing...\n" + sat);
        System.out.println();
        System.out.println("Value for (B,b): " + sat.get("B", "b"));
        System.out.println("Value for (B,c): " + sat.get("B", "c"));
        System.out.println("Value for (D,+): " + sat.get("D", "+"));
        System.out.println("Value for (D,*): " + sat.get("D", "*"));
        System.out.println("Removing (B,b)...");
        sat.remove("B", "b");
//        System.out.println("Showing...\n" + sat);
        System.out.println("Removing (B,+)...");
        sat.remove("B", "+");
//        System.out.println("Showing...\n" + sat);
        System.out.println("Is (A,a) error? " + sat.isError("A", "a"));
        System.out.println("Is (F,j) error? " + sat.isError("F", "j"));
        System.out.println("Is (A,b) error? " + sat.isError("A", "b"));
        System.out.println("Terminals: " + sat.getTerminals());
        System.out.println("NON-Terminals: " + sat.getNonTerminals());
        System.out.println(sat);
    }
}
